﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Helpers;
using QFramework;
using UnityEngine;

namespace Gp.Scripts.Core
{
    public class GameEntry : MonoBehaviour
    {
        public static GameEntry Instance { get; private set; }
        public bool IsInit { get; private set; }
        public List<BaseGameManager> Managers { get; private set; }
        private Transform Persistent { get; set; }


        private void Awake()
        {
            // 激活懒加载
            TryInit();
        }

        private void TryInit()
        {
            // 激活懒加载
            var mainArchitecture = MainArchitecture.Interface;
            
            if (IsInit) return;
            IsInit = true;


            if (Instance != null) throw new ApplicationException("致命错误：同时加载了多个GameEntry, 请检查操作是否规范。");
            Instance = this;
            DontDestroyOnLoad(this);

            Managers = new List<BaseGameManager>();

            // 资源初始化
            var resManagerObject        = new GameObject($"{nameof(Res.ResPackageManager)}");
            var resManager = resManagerObject.AddComponent<Res.ResPackageManager>();
            DontDestroy(resManagerObject);
            resManager.Init();
            StartCoroutine(resManager.InitPackage(() =>
            {
                AppDomain.CurrentDomain.GetAssemblies()
                    .Where(x => x != null && x.GetName().Name.StartsWith("Gp"))
                    .ForEach(LoadManager);
                
                
                foreach (var system in MainArchitecture.Interface.Systems())
                {
                    if (system is not BaseGameSystem obj) continue;
                    CommonUtils.SafeCall(obj.OnStart, system.GetType().FullName, "Error occured when start");
                }

                Global.SendEvent<Event_System.GameStart>();
                
                if (this.TryGetComponent<ShowStartScreen>(out var startScreen))
                {
                    startScreen.Show();
                }
            }));
        }


        private void LoadManager(Assembly assembly)
        {
            #region 载入 Configuration

            var configurations = assembly
                .GetTargetType(baseClass: typeof(ISystemConfig), attribute: typeof(AutoLoadAttribute))
                .Select(i => Activator.CreateInstance(i) as ISystemConfig)
                .ForEach(x => MainArchitecture.Interface.RegisterConfig(x));

            #endregion

            #region 载入 Manager

            // 注册所有 Manager 组件
            var managers = assembly
                .GetTargetType(baseClass: typeof(BaseGameManager), attribute: typeof(AutoLoadAttribute))
                .Select(Activator.CreateInstance)
                .OfType<BaseGameManager>()
                .ForEach(x =>
                {
                    MainArchitecture.Interface.RegisterSystem(x);
                    Managers.Add(x);
                });
            // Managers.AddRange(managers);

            #endregion

            #region 载入 System 组件

            // 注册所有System组件
            assembly.GetTargetType(baseClass: typeof(BaseGameSystem), attribute: typeof(AutoLoadAttribute))
                .Select(Activator.CreateInstance)
                .ForEach(x => MainArchitecture.Interface.RegisterSystem(x as ISystem));

            #endregion
        }


        private void Update()
        {
            foreach (var m in Managers)
            {
                CommonUtils.SafeCall(() => m.OnUpdate(), m.GetType().Name, "Error occured when executing Update");
            }
        }


        /// <summary>
        /// 手动载入GameEntry进行初始化
        /// </summary>
        public static void ManualInit()
        {
            // 载入 GameEntry
            if (Instance) return;
            new GameObject("GameEntry").AddComponent<GameEntry>().TryInit();
        }


        public void DontDestroy(Transform trans)
        {
            if (!Persistent)
            {
                Persistent = new GameObject("Persistent").transform;
                Persistent.SetParent(transform);
            }

            trans.SetParent(Persistent);
        }

        public void DontDestroy(GameObject go)
        {
            DontDestroy(go.transform);
        }
    }
}